﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Tycho.Runtime;
using System.Reflection.Emit;
using System.Reflection;
using Tycho.Language;

namespace TychoTest {
    /// <summary>
    /// Summary description for TestCodeGeneration
    /// </summary>
    [TestClass]
    public class TestNativeMethodLoader {
        public static int Plus3 (int a, int b, bool c) {
            return c? a + b: a - b;
        }

        public static int Plus (int a, int b) {
            return a + b;
        }

        [TestMethod]
        public void TestPlus () {
            NativeFunctionDelegate plus = NativeMethodLoader.CreateNativeMethod (typeof (TestNativeMethodLoader).GetMethod ("Plus3"));
            AnyObject r = plus (4, 12, false);
            Assert.AreEqual (-8, r.ExpectValue<int> ());
        }

        public static void ReturnVoid (int x) {
            // do nothing, just return void
        }

        [TestMethod]
        public void TestVoidReturn () {
            NativeFunctionDelegate returnVoid = NativeMethodLoader.CreateNativeMethod (typeof (TestNativeMethodLoader).GetMethod ("ReturnVoid"));
            AnyObject r = returnVoid (RuntimeModule.CreateInteger (4));
            Assert.AreEqual (RuntimeModule.Null, r);
        }

        public static void AddToList (List<int> l, int a, int b) {
            l.Add (a);
            l.Add (b);
        }

        [TestMethod]
        public void TestNativeArgument () {
            NativeFunctionDelegate addToList = NativeMethodLoader.CreateNativeMethod (typeof (TestNativeMethodLoader).GetMethod ("AddToList"));
            List<int> l = new List<int> ();
            AnyObject r = addToList (new NativeObject<List<int>> (l), RuntimeModule.CreateInteger (4), RuntimeModule.CreateInteger (12));
            Assert.AreEqual (2, l.Count);
            Assert.AreEqual (4, l [0]);
            Assert.AreEqual (12, l [1]);
        }

        public static List<string> ReturnList () {
            List<string> l = new List<string> ();
            l.Add ("one");
            l.Add ("two");
            return l;
        }

        [TestMethod]
        public void TestReturnNativeNoArguments () {
            NativeFunctionDelegate addToList = NativeMethodLoader.CreateNativeMethod (typeof (TestNativeMethodLoader).GetMethod ("ReturnList"));
            List<string> l = addToList ().ExpectNative<List<string>> ();
            Assert.AreEqual (2, l.Count);
            Assert.AreEqual ("one", l [0]);
            Assert.AreEqual ("two", l [1]);
        }

        class TestProxyObject : AnyObject {
            AnyObject Target;

            public TestProxyObject (AnyObject target) {
                Target = target;
            }

            public override AnyObject InvokeMethod (AnyObject self, AnyObject name, params AnyObject [] arguments) {
                return Target.InvokeMethod (self, name, arguments);
            }

            public override bool TryCastTo<T> (out T result) {
                return Target.TryCastTo<T> (out result);
            }
        }

        /// <summary>
        /// If an incoming object is a proxy object, test that the proxy
        /// is converted to the proxied target.
        /// </summary>
        [TestMethod]
        public void TestValueProxyObjectConversion () {
            NativeFunctionDelegate plus = NativeMethodLoader.CreateNativeMethod (typeof (TestNativeMethodLoader).GetMethod ("Plus"));
            AnyObject r = plus (new TestProxyObject (4), new TestProxyObject (12));
            Assert.AreEqual (16, r.ExpectValue<int> ());
        }

        /// <summary>
        /// If an incoming object is a proxy object, test that the proxy
        /// is converted to the proxied target.
        /// </summary>
        [TestMethod]
        public void TestNativeProxyObjectConversion () {
            NativeFunctionDelegate addToList = NativeMethodLoader.CreateNativeMethod (typeof (TestNativeMethodLoader).GetMethod ("AddToList"));
            List<int> l = new List<int> ();
            AnyObject r = addToList (new TestProxyObject (new NativeObject<List<int>> (l)), 4, 12);
            Assert.AreEqual (2, l.Count);
            Assert.AreEqual (4, l [0]);
            Assert.AreEqual (12, l [1]);
        }

        public static double PlusReal (int self, double arg) {
            return self + arg;
        }

        [TestMethod]
        public void TestRealValueConversion () {
            NativeFunctionDelegate plusReal = NativeMethodLoader.CreateNativeMethod (typeof (TestNativeMethodLoader).GetMethod ("PlusReal"));
            AnyObject r = plusReal (5, 5.6);
            Assert.AreEqual ((AnyObject) 10.6, r);
        }
    }
}
